package com.thp.proxy.custom;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

/**
 * 自定义的Proxy类
 *
 * 实现的功能:
 * 	  生成代理对象的代码
 * @author Tommy
 *
 */
public class MyProxy {

    // 换行
    private static String ln = "\r\n";


    // 获取代理对象的方法
    public static Object newProxyInstance(MyClassLoader classLoader, Class<?>[] interfaces, MyInvocationHandler h) {


        try {

            // 1,生成源代码
            String proxySrc = generateSrc(interfaces[0]);

            // 2,将生成的源代码输出到磁盘保存为.java文件
            // 必须生成在   com.thp.proxy.custom  这个包下

            String filePath = MyProxy.class.getResource("").getPath();
            File f = new File(filePath + "$Proxy0.java");
            FileWriter fw = new FileWriter(f);
            fw.write(proxySrc);
            fw.flush();
            fw.close();



            // 3,编译源代码, 并且生成.class文件
            JavaCompiler complier = ToolProvider.getSystemJavaCompiler();  // 编译器

            StandardJavaFileManager manager = complier.getStandardFileManager(null, null, null);

            Iterable iterable = manager.getJavaFileObjects(f);

            CompilationTask task = complier.getTask(null, manager, null, null, null, iterable);

            task.call();
            manager.close();


            // 4,将class文件中的内容动态加载到JVM中来



            // 5,返回被代理后的代理对象
            Class proxyClass = classLoader.findClass("$Proxy0");
            // 获取构造方法
            Constructor c = proxyClass.getConstructor(MyInvocationHandler.class);

            // 删除生成的.java文件
            // f.delete();

            // 通过MyClassLoader将$MyProxy加载到JVM中来
            return c.newInstance(h);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    // 生成源代码
    // 千万要注意  不能错一个字,错了的话,就可能编译通不过
    private static String generateSrc(Class<?> interfaces) {

        StringBuffer src = new StringBuffer();
        src.append("package com.thp.proxy.custom;" + ln);
        // 导包
        src.append("import java.lang.reflect.Method;" + ln);

        src.append("public class $Proxy0 implements " + interfaces.getName() + "{" + ln);


        // 持有  MyInvocationHandler 的引用
        src.append("MyInvocationHandler h;" + ln);

        // 构造方法
        src.append("public $Proxy0(MyInvocationHandler h) {" + ln);
        src.append("this.h = h;" + ln);
        src.append("}" + ln);
        int i=0;
        // 将方法循环出来
        for(Method m : interfaces.getMethods()) {
            src.append("public " + m.getReturnType().getName() + " " + m.getName() + "() {" + ln);

            src.append("try {" + ln);

            src.append("Method m"+i+" = " + interfaces.getName() +".class.getMethod(\""+ m.getName() +"\",new Class[]{});" + ln);
            // 调用方法  (简化操作没有参数)
            src.append("this.h.invoke(this,m"+i+",null);" + ln);

            src.append("} catch(Throwable e) { e.printStackTrace(); }" + ln);
            // 返回值处理   我这里简化了操作,没有返回值
			/*if(!m.getReturnType().getName().equals("void")) {
				src.append("return ");
			}*/

            src.append("}" + ln);
            i++;
        }




        src.append("}");




        return src.toString();
    }




}
